perm filename DEFCLA.L[FTL,LSP] blob sn#826383 filedate 1986-10-21 generic text, type T, neo UTF8
;;;-*-Mode:LISP; Package: PCL; Base:10; Syntax:Common-lisp -*-
;;;
;;; *************************************************************************
;;; Copyright (c) 1985 Xerox Corporation.  All rights reserved.
;;;
;;; Use and copying of this software and preparation of derivative works
;;; based upon this software are permitted.  Any distribution of this
;;; software or derivative works must comply with all applicable United
;;; States export control laws.
;;; 
;;; This software is made available AS IS, and Xerox Corporation makes no
;;; warranty about the software, its performance or its conformity to any
;;; specification.
;;; 
;;; Any person obtaining a copy of this software is requested to send their
;;; name and post office or electronic mail address to:
;;;   CommonLoops Coordinator
;;;   Xerox Artifical Intelligence Systems
;;;   2400 Hanover St.
;;;   Palo Alto, CA 94303
;;; (or send Arpanet mail to CommonLoops-Coordinator.pa@Xerox.arpa)
;;;
;;; Suggestions, comments and requests for improvements are also welcome.
;;; *************************************************************************
;;;

(in-package 'pcl)


  ;;   
;;;;;; New New Minglewood Blues
  ;;   the new "legendary macro itself"
;;;
(defmacro ndefstruct (name-and-options &rest slot-descriptions)
  ;;
  ;; The defstruct macro does some pre-processing on name-and-options and
  ;; slot-descriptions before it passes them on to expand-defstruct. It
  ;; also pulls out the documentation string (if there is one) and passes
  ;; it to expand defstruct as a separate argument.
  ;;
  ;; The main reason for doing this is that it imposes more uniformity in
  ;; the syntax of defstructs for different metaclasses, and it puts some
  ;; useful error checking for that syntax in one central place.
  ;; 
  (let ((documentation (and (stringp (car slot-descriptions))
			    (pop slot-descriptions))))
    (or (listp name-and-options) (setq name-and-options (list name-and-options)))
    (setq slot-descriptions
          (iterate ((sd in slot-descriptions))
            (collect
              (cond ((not (listp sd)) (list sd nil))
                    (t (unless (evenp (length sd))
                         (error "While parsing the defstruct ~S, the slot-description: ~S~%~
                                 has an odd number of elements."
                                (car name-and-options) sd))
                       sd)))))
    (keyword-parse ((class 'structure))
                   (cdr name-and-options)
      (let ((class-object (class-named class t)))
        (if class-object
            (expand-defstruct
              (class-prototype class-object) name-and-options documentation slot-descriptions)
            (error "The argument to defstruct's :class option was ~S;~%~
                    but there is no class named ~S."
                   class class))))))

(defmacro defclass (name slots includes &rest options)
  `(ndefstruct (,name (:class class) (:include ,includes) ,@options)
	       ,@slots))

(defmeth expand-defstruct ((class basic-class) name-and-options documentation slot-descriptions)
  (ignore documentation)
  (let* ((name (car name-and-options))
         (ds-options (parse-defstruct-options class name (cdr name-and-options)))
         (slotds (parse-slot-descriptions class ds-options slot-descriptions)))
    `(progn
       (eval-when (load eval)	 
	 (record-definition ',name 'ndefstruct))
       ;; Start by calling add-named-class which will actually define the new
       ;; class, updating the class lattice obsoleting old instances etc.
       (eval-when (compile load eval)
         (add-named-class
	   (class-prototype (class-named ',(class-name (class-of class))))
	   ',name
	   ',(or (ds-options-includes ds-options)
		 (class-default-includes class))
	   ',slotds
	   ',ds-options))
       ,@(expand-defstruct-make-definitions class name ds-options slotds)
       ',name)))

(defmeth expand-defstruct-make-definitions ((class basic-class)
					     name ds-options slotds)
  (append (make-accessor-definitions class name ds-options slotds)
          (make-constructor-definitions class name ds-options slotds)
          (make-copier-definitions class name ds-options slotds)
          (make-predicate-definitions class name ds-options slotds)
          (make-print-function-definitions class name ds-options slotds)))

(define-function-template iwmc-class-accessor () '(slot-name)
  `(function (lambda (iwmc-class) (get-slot--class iwmc-class slot-name))))

(eval-when (load)
  (pre-make-templated-function-constructor iwmc-class-accessor))

(define-function-template iwmc-class-accessor-setf (read-only-p) '(slot-name)
  (if read-only-p
      `(function
         (lambda (iwmc-class new-value)
	   (error "~S is a read only slot." slot-name)))
      `(function
         (lambda (iwmc-class new-value)
	   (put-slot--class iwmc-class slot-name new-value)))))


(eval-when (load)
  (pre-make-templated-function-constructor iwmc-class-accessor-setf nil)
  (pre-make-templated-function-constructor iwmc-class-accessor-setf t))

(defun make-iwmc-class-accessor (slotd)
  (funcall (get-templated-function-constructor 'iwmc-class-accessor)
	   (slotd-name slotd)))

(defun make-iwmc-class-accessor-setf (slotd)
  (funcall
    (get-templated-function-constructor 'iwmc-class-accessor-setf
					(slotd-read-only slotd))
    (slotd-name slotd)))

(defun add-named-method-early (discriminator-name
			       arglist
			       argument-specifiers
			       function)
  (if (null *real-methods-exist-p*)
      (unless (memq discriminator-name *protected-early-selectors*)
	(setf (symbol-function discriminator-name) function))
      (add-named-method (class-prototype (class-named 'discriminator))
			(class-prototype (class-named 'method))
			discriminator-name
			arglist
			argument-specifiers
			()
			function)))
  
(defmeth make-accessor-definitions
	 ((class basic-class) name ds-options slotds)
  (ignore class ds-options)
  (cons `(do-accessor-definitions ',name ',slotds)
	(iterate ((slotd in slotds))
	  (let ((accessor (slotd-accessor slotd))
		setf-discriminator-name)
	    (when accessor
	      (setq setf-discriminator-name
		    (make-setf-discriminator-name accessor))
	      (compile-time-define 'defun accessor)
	      (compile-time-define 'defun setf-discriminator-name)
	      (compile-time-define 'defsetf accessor setf-discriminator-name)
	      (collect `(defsetf ,accessor ,setf-discriminator-name)))))))

(defun do-accessor-definitions (name slotds)
  (let ((class (class-named name))
	(accessor nil)
	(setf-discriminator-name nil))
    (dolist (slotd slotds)
      (when (setq accessor (slotd-accessor slotd))
	(setq setf-discriminator-name
	      (make-setf-discriminator-name accessor))
	(unless *real-methods-exist-p*
	  (record-early-discriminator accessor)
	  (record-early-discriminator setf-discriminator-name))
	(add-named-method-early accessor
				`(,name)
				`(,class)
				(or (slotd-get-function slotd)
				    (make-iwmc-class-accessor slotd)))
	(add-named-method-early setf-discriminator-name
				`(,name new-value)
				`(,class)
				(or (slotd-put-function slotd)
				    (make-iwmc-class-accessor-setf
				      slotd)))))
    (unless *real-methods-exist-p*
      (record-early-method-fixup
	`(let ((*real-methods-exist-p* t))
	   (do-accessor-definitions ',name ',slotds))))))

(defmeth make-constructor-definitions ((class basic-class) name ds-options slotds)
  (ignore class slotds)
  (let ((constructors (ds-options-constructors ds-options)))
    (iterate ((constructor in constructors))
      (when (car constructor)
        (collect
          (if (cdr constructor)
              `(defun ,(car constructor) ,(cadr constructor)
                 (make ',name ,@(iterate ((slot-name in (cadr constructor)))
                                         (unless (memq slot-name
                                                       '(&optional &rest &aux))
                                           (collect `',(make-keyword slot-name))
                                           (collect slot-name)))))
              `(defun ,(car constructor) (&rest init-plist)
                 (apply #'make ',name init-plist))))))))

(define-function-template copier--class () ()
  `(function
     (lambda (iwmc-class)
       (let* ((class (class-of iwmc-class))
              (to (make-instance (class-of iwmc-class)))
              (from-static (iwmc-class-static-slots iwmc-class))        
              (to-static (iwmc-class-static-slots to))
              (static-slots (class-instance-slots class)))
         (do ((i 0 (+ i 1))
              (x static-slots (cdr x)))
             ((null x))
           (setf (%static-slot-storage-get-slot--class to-static i)
                 (%static-slot-storage-get-slot--class from-static i)))
         (setf (iwmc-class-dynamic-slots to)
               (copy-list (iwmc-class-dynamic-slots iwmc-class)))
         to))))

(eval-when (load)
  (pre-make-templated-function-constructor copier--class))

(defmeth make-copier-definitions ((class basic-class) name ds-options slotds)
  (ignore slotds)
  (let ((copier (ds-options-copier ds-options)))    
    (when copier
      (compile-time-define 'defun copier)
      `((do-copier-definition ',name ',copier)))))

(defun do-copier-definition (class-name copier-name)
  (unless *real-methods-exist-p*
    (record-early-discriminator copier-name)
    (record-early-method-fixup
      `(let ((*real-methods-exist-p* t))
	 (do-copier-definition ',class-name ',copier-name))))
  (add-named-method-early copier-name
			  `(,class-name)
			  `(,(class-named class-name))
			  (funcall
			    (get-templated-function-constructor
			      'copier--class))))

(define-function-template iwmc-class-predicate () '(class-name)
  `(function (lambda (x)
	       (and (iwmc-class-p x)
		    (typep--class x class-name)))))

(eval-when (load)
  (pre-make-templated-function-constructor iwmc-class-predicate))

(defmeth make-predicate-definitions ((class basic-class)
				     name ds-options slotds)
  (ignore class slotds)
  (let ((predicate (or (ds-options-predicate ds-options)
                       (make-symbol (string-append name " Predicate")))))
    (compile-time-define 'defun predicate)
    `((do-predicate-definition ',name ',predicate)
      (deftype ,name () '(satisfies ,predicate)))))

(defun do-predicate-definition (class-name predicate-name)
  (setf (symbol-function predicate-name)
	(funcall (get-templated-function-constructor 'iwmc-class-predicate)
		 class-name)))

(defun make-print-function-definitions
	  (class name ds-options slotds)
  (ignore class slotds)
  (let* ((print-function (ds-options-print-function ds-options))
	 (arglist ())
	 (defun ())
	 (defun-name ()))
    (when print-function
      (cond ((symbolp print-function)
	     (setq arglist '(object stream depth)))
	    ((and (listp print-function) (eq (car print-function) 'lambda))
	     (setq arglist (cadr print-function)
		   defun-name (intern 
				(string-append (symbol-name name)
					       " Print Function"))
		   defun `(defun ,defun-name ,arglist
			    ,@(cddr print-function))
		   print-function defun-name))
	    (t
	     (error "Internal error, make-print-function-definitions can't~%~
                     understand the contents of the print-function slot of~%~
                     the ds-options.")))
      `(,defun
	(do-print-function-definitions ',name ',arglist ',print-function)))))

(defun do-print-function-definitions (name arglist print-function)
  (unless *real-methods-exist-p*
    (record-early-method-fixup
      `(let ((*real-methods-exist-p* t))
	 (do-print-function-definitions ',name ',arglist ',print-function))))
  (add-named-method-early 'print-instance
			  arglist
			  (list (class-named name))
			  print-function))